﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SwiftMVVM.ChangeTracking;
using System.ComponentModel;
using System.Windows;

namespace SwiftMVVM.Commands
{
    /// <summary>
    /// ChangeManagerCommands is a set of commands that can be directly applied to 
    /// any exposed change manager object.
    /// </summary>
    public static class ChangeManagerCommands
    {
        /// <summary>
        /// Issue the AcceptChanges call to the ChangeManager which
        /// tells it to mark the current state of the object it's watching as
        /// the Accepted state
        /// </summary>
        public static RelayCommand<ChangeManager> AcceptChanges
        {
            get
            {
                //The first line here says to fire AcceptChanges on the passed in ChangeManager
                //if it's not null.
                //The second line controls whether or not the command itself is enabled.
                //So here we only enable the command if the passed in changemanager is not null
                //and if and only if it HasChanges, as there is no need to AcceptChanges on an object with 
                //no changes
                return new RelayCommand<ChangeManager>(changeManager =>
                { if (changeManager != null) { changeManager.AcceptChanges(); } },
                changeManager => { return changeManager != null && changeManager.HasChanges; });
            }
        }

        /// <summary>
        /// Will undo the last (optionally n) changes to the object in the order
        /// that the changes were made.
        /// This command can be called with 1, or 2 parameters.
        /// The first parameter must always be a ChangeManager.
        /// Optionally, a second parameter (int) can be passed in to specify
        /// that more than one change should be undone
        /// </summary>
        public static RelayCommand<object> Undo
        {
            get
            {
                //The first section here says to fire Undo on the passed in ChangeManager
                //if it's not null. If there are two objects in the parameter argument, then
                //we assume the second argument in the Count of how many changes to roll back.
                //The second line controls whether or not the command itself is enabled.
                //So here we only enable the command if the passed in changemanager is not null
                //and if and only if it HasChanges, as there is no need to Undo on an object with 
                //no changes
                return new RelayCommand<object>(parameter =>
                {
                    var parameters = parameter as object[];
                    if (parameters != null)
                    {
                        if (parameters.Length != 2) { throw new InvalidOperationException("Must pass no more than 2 parameters to ChangeManagerCommands.Undo"); }
                        ((ChangeManager)parameters[0]).Undo((((int?)parameters[1]) ?? 0) + 1);
                    }
                    else { ((ChangeManager)parameter).Undo(); }
                },
                parameter =>
                {
                    var parameters = parameter as object[];
                    if (parameters != null)
                    {
                        if (parameters.Length != 2) { throw new InvalidOperationException("Must pass no more than 2 parameters to ChangeManagerCommands.Undo"); }
                        var changeManager = parameters[0] as ChangeManager;
                        return changeManager != null && changeManager.HasChanges;
                    }
                    else
                    {
                        var changeManager = parameter as ChangeManager;
                        return changeManager != null && changeManager.HasChanges; 
                    }
                });
            }
        }

        /// <summary>
        /// Will redo the last (optionally n) changes to the object in the order
        /// that the changes were made.
        /// This command can be called with 1, or 2 parameters.
        /// The first parameter must always be a ChangeManager.
        /// Optionally, a second parameter (int) can be passed in to specify
        /// that more than one change should be reapplied
        /// </summary>
        public static RelayCommand<object> Redo
        {
            get
            {
                //The first section here says to fire Redo on the passed in ChangeManager
                //if it's not null. If there are two objects in the parameter argument, then
                //we assume the second argument in the Count of how many changes to reapply.
                //The second line controls whether or not the command itself is enabled.
                //So here we only enable the command if the passed in changemanager is not null
                //and if and only if it actually has items in it's available redo list
                return new RelayCommand<object>(parameter =>
                {
                    var parameters = parameter as object[];
                    if (parameters != null)
                    {
                        if (parameters.Length != 2) { throw new InvalidOperationException("Must pass no more than 2 parameters to ChangeManagerCommands.Undo"); }
                        ((ChangeManager)parameters[0]).Redo((((int?)parameters[1]) ?? 0) + 1);
                    }
                    else { ((ChangeManager)parameter).Redo(); }
                },
                parameter =>
                {
                    var parameters = parameter as object[];
                    if (parameters != null)
                    {
                        if (parameters.Length != 2) { throw new InvalidOperationException("Must pass no more than 2 parameters to ChangeManagerCommands.Undo"); }
                        var changeManager = parameters[0] as ChangeManager;
                        return changeManager != null && changeManager.RedoList.Count() > 0;
                    }
                    else 
                    {
                        var changeManager = parameter as ChangeManager;
                        return changeManager != null && changeManager.RedoList.Count() > 0;
                    }
                });
            }
        }
    }
}
